#ifndef LOGGER_H
#define LOGGER_H
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include <iostream>
#include <memory>
#include <sstream>
#include <string>
class mylogger;
#define LOG_ANY(level, file, line, function, message)  \
    {                                                  \
        std::ostringstream oss;                        \
        format_logheader(file, line, function, oss);   \
        oss << " - " << message;                       \
        SPDLogger::instance().write(level, oss.str()); \
    }

#define LOG_TRACE(_LEVEL_, MSG, ...)                             \
    {                                                            \
        std::ostringstream oss;                                  \
        format_logheader(__FILE__, __LINE__, __FUNCTION__, oss); \
        oss << " - " << format_string(MSG, ##__VA_ARGS__);       \
        SPDLogger::instance().write(_LEVEL_, oss.str());         \
    }

#define LOGGER_TRACE_FORMAT(_LOGGER_, _MSG_, _LEVEL_)            \
    {                                                            \
        std::ostringstream oss;                                  \
        format_logheader(__FILE__, __LINE__, __FUNCTION__, oss); \
        oss << " - " << _MSG_;                                   \
        _LOGGER_.write(_LEVEL_, oss.str());                      \
    }

#define LOGGER_LEVEL_DEBUG 1
#define LOGGER_LEVEL_INFO 2
#define LOGGER_LEVEL_WARN 3
#define LOGGER_LEVEL_ERROR 4
#define LOGGER_LEVEL_FATAL 5

#define LOG_DEBUG(MSG, ...) LOG_TRACE(LOGGER_LEVEL_DEBUG, MSG, ##__VA_ARGS__)
#define LOG_INFO(MSG, ...) LOG_TRACE(LOGGER_LEVEL_INFO, MSG, ##__VA_ARGS__)
#define LOG_WARN(MSG, ...) LOG_TRACE(LOGGER_LEVEL_WARN, MSG, ##__VA_ARGS__)
#define LOG_ERROR(MSG, ...) LOG_TRACE(LOGGER_LEVEL_ERROR, MSG, ##__VA_ARGS__)
#define LOG_FATAL(MSG, ...) LOG_TRACE(LOGGER_LEVEL_FATAL, MSG, ##__VA_ARGS__)

#define LOGGER_DEBUG(_X_) LOGGER_TRACE_FORMAT(SPDLogger::instance(), _X_, LOGGER_LEVEL_DEBUG)
#define LOGGER_INFO(_X_) LOGGER_TRACE_FORMAT(SPDLogger::instance(), _X_, LOGGER_LEVEL_INFO)
#define LOGGER_WARN(_X_) LOGGER_TRACE_FORMAT(SPDLogger::instance(), _X_, LOGGER_LEVEL_WARN)
#define LOGGER_ERROR(_X_) LOGGER_TRACE_FORMAT(SPDLogger::instance(), _X_, LOGGER_LEVEL_ERROR)
#define LOGGER_FATAL(_X_) LOGGER_TRACE_FORMAT(SPDLogger::instance(), _X_, LOGGER_LEVEL_FATAL)

static void format_logheader(const char *file, int line, const char *func, std::ostringstream &oss)
{
    auto getFileName = [](const char *file)
    {
        auto pos = strrchr(file, '/');
#ifdef _WIN32
        if (!pos)
        {
            pos = strrchr(file, '\\');
        }
#endif
        return pos ? pos + 1 : file;
    };

    auto getFunctionName = [](const char *func)
    {
#ifndef _WIN32
        return func;
#else
        auto pos = strrchr(func, ':');
        return pos ? pos + 1 : func;
#endif
    };

    ;
    getFunctionName(func);

    oss << getFileName(file) << ":" << line << " " << getFunctionName(func);
}
static std::string format_string(const char *fmt, ...)
{
    const static int max_log_size = 4096;
    char buf[max_log_size];
    va_list va;
    va_start(va, fmt);

    int ret = vsnprintf(buf, max_log_size, fmt, va);
    va_end(va);
    return buf;
}

class SPDLogger
{
   public:
   private:
    SPDLogger();
    ~SPDLogger();

   public:
    SPDLogger(const SPDLogger &logger) = delete;
    SPDLogger &operator=(const SPDLogger &logger) = delete;

   public:
    static SPDLogger &instance();

    void init(const std::string &name, const std::string &filepath, int level);
    void setLoggerLevel(int level);
    void setStdoutLoggerLevel(int level);

    void uninit();

    void write(int level, const std::string &logText);

   private:
    std::shared_ptr<mylogger> my_logger_;
};
#endif  // LOG_H
